﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Configuration;

namespace CodeGeneratorLibrary
{
	public class Settings
	{
		Options options = Options.Factory;
		public Settings()
		{
			_connectionString = options.ConnectionString;
		}

		private string _connectionString = string.Empty;//Options.ConnectionString;

		const string Namespace = "Galssoft.SapEvents.Entities";

		//This is the name of your database and is used in naming
		//the repository. By default we set it to the connection string name
		const string DatabaseName = "Northwind";
		const string DefaultConnectionString="Data Source=.\\SQLEXPRESS;Initial Catalog=cargob2b;Integrated Security=true";

		const bool TreatTinyint1AsBool = false;

		//this is a list of tables you don't want generated
		public static string[] ExcludeTables = new string[]{
    "sysdiagrams",
    "BuildVersion",
    };

		public static string CleanUp(string tableName)
		{
			string result = tableName;

			//strip blanks
			result = result.Replace(" ", "");

			//put your logic here...

			return result;
		}

		string CheckNullable(Column col)
		{
			string result = "";
			if (col.IsNullable && col.SysType != "byte[]" && col.SysType != "string")
				result = "?";
			return result;
		}
		string GetConnectionString(string connectionStringName)
		{
			var _CurrentProject = GetCurrentProject();

			string result = "";
			ExeConfigurationFileMap configFile = new ExeConfigurationFileMap();
			configFile.ExeConfigFilename = GetConfigPath();

			if (string.IsNullOrEmpty(configFile.ExeConfigFilename))
				throw new ArgumentNullException("The project does not contain App.config or Web.config file.");


			var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
			var connSection = config.ConnectionStrings;

			//if the connectionString is empty - which is the defauls
			//look for count-1 - this is the last connection string
			//and takes into account AppServices and LocalSqlServer
			if (string.IsNullOrEmpty(connectionStringName))
			{
				if (connSection.ConnectionStrings.Count > 1)
				{
					result = connSection.ConnectionStrings[connSection.ConnectionStrings.Count - 1].ConnectionString;
				}
			}
			else
			{
				try
				{
					result = connSection.ConnectionStrings[connectionStringName].ConnectionString;
				}
				catch
				{
					result = "There is no connection string name called '" + connectionStringName + "'";
				}
			}

			return result;
		}

		//string _connectionString="Data Source=.\\SQLEXPRESS;Initial Catalog=cargob2b;Integrated Security=true";
		public string ConnectionString
		{
			get
			{
				if (String.IsNullOrEmpty(_connectionString))
				{

					_connectionString = GetConnectionString(DefaultConnectionString);

				}

				if (_connectionString.Contains("|DataDirectory|"))
				{
					//have to replace it
					string dataFilePath = GetDataDirectory();
					_connectionString = _connectionString.Replace("|DataDirectory|", dataFilePath);
				}

				return _connectionString;
			}
		}

		public EnvDTE.Project GetCurrentProject()
		{
			//разобратся
			throw new Exception("Host property returned unexpected value (null)");

/*
			IServiceProvider _ServiceProvider = (IServiceProvider)Host;
			if (_ServiceProvider == null)
				throw new Exception("Host property returned unexpected value (null)");

			EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE));
			if (dte == null)
				throw new Exception("Unable to retrieve EnvDTE.DTE");

			Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
			if (activeSolutionProjects == null)
				throw new Exception("DTE.ActiveSolutionProjects returned null");

			EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
			if (dteProject == null)
				throw new Exception("DTE.ActiveSolutionProjects[0] returned null");

			return dteProject;
*/

		}

		private string GetProjectPath()
		{
			EnvDTE.Project project = GetCurrentProject();
			System.IO.FileInfo info = new System.IO.FileInfo(project.FullName);
			return info.Directory.FullName;
		}

		private string GetConfigPath()
		{
			EnvDTE.Project project = GetCurrentProject();
			foreach (EnvDTE.ProjectItem item in project.ProjectItems)
			{
				// if it is the app.config file, then open it up
				if (item.Name.Equals("App.config", StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config", StringComparison.InvariantCultureIgnoreCase))
					return GetProjectPath() + "\\" + item.Name;
			}
			return String.Empty;
		}

		public string GetDataDirectory()
		{
			EnvDTE.Project project = GetCurrentProject();
			return System.IO.Path.GetDirectoryName(project.FileName) + "\\App_Data\\";
		}
	}

	public class Table
	{

		public List<Column> Columns;
		public List<FKTable> FKTables;
		public string Name;
		public string CleanName;
		public string ClassName;
		public string PrimaryKey;
		public string Schema;
		public string QueryableName;
		public string EntityName;

		public bool HasLogicalDelete()
		{
			return this.Columns.Any(x => x.Name.ToLower() == "deleted" || x.Name.ToLower() == "isdeleted");
		}
		public Column DeleteColumn
		{
			get
			{
				Column result = null;
				if (this.Columns.Any(x => x.Name.ToLower() == "deleted"))
					result = this.Columns.Single(x => x.Name.ToLower() == "deleted");
				if (this.Columns.Any(x => x.Name.ToLower() == "isdeleted"))
					result = this.Columns.Single(x => x.Name.ToLower() == "isdeleted");
				return result;
			}
		}
		public Column PK
		{
			get
			{
				return this.Columns.FirstOrDefault(x => x.IsPK) ?? this.Columns[0];
			}
		}
		public Column Descriptor
		{
			get
			{
				if (this.Columns.Count == 1)
				{
					return this.Columns[0];
				}
				else
				{
					//get the first string column
					Column result = null;
					result = this.Columns.FirstOrDefault(x => x.SysType.ToLower().Trim() == "string");
					if (result == null)
						result = this.Columns[1];
					return result;
				}
			}
		}
	}

	public class Column
	{
		public string Name;
		public string CleanName;
		public string SysType;
		public string DataType;
		public SqlDbType SqlDbType;
		public bool AutoIncrement;
		public bool IsPK;
		public int MaxLength;
		public bool IsNullable;
		public bool IsForeignKey;
		public bool IsUnsigned;
	}
	public class FKTable
	{
		public string ThisTable;
		public string ThisColumn;
		public string OtherTable;
		public string OtherColumn;
		public string OtherClass;
		public string OtherQueryable;
	}

	public class SP
	{
		public string Name;
		public string CleanName;
		public string ClassName;
		public List<SPParam> Parameters;
		public SP()
		{
			Parameters = new List<SPParam>();
		}
		public string ArgList
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				int loopCount = 1;
				foreach (var par in Parameters)
				{
					sb.AppendFormat("{0} {1}", par.SysType, par.CleanName);
					if (loopCount < Parameters.Count)
						sb.Append(",");
					loopCount++;
				}
				return sb.ToString();
			}
		}
	}
	public class SPParam
	{
		public string Name;
		public string CleanName;
		public string SysType;
		public string SqlDbType;
	}


	/*
	 * SubSonic - http://subsonicproject.com
	 * 
	 * The contents of this file are subject to the New BSD
	 * License (the "License"); you may not use this file
	 * except in compliance with the License. You may obtain a copy of
	 * the License at http://www.opensource.org/licenses/bsd-license.php
	 * 
	 * Software distributed under the License is distributed on an 
	 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
	 * implied. See the License for the specific language governing
	 * rights and limitations under the License.
	*/

	/// <summary>
	/// Summary for the Inflector class
	/// </summary>
	public static class Inflector
	{
		private static readonly List<InflectorRule> _plurals = new List<InflectorRule>();
		private static readonly List<InflectorRule> _singulars = new List<InflectorRule>();
		private static readonly List<string> _uncountables = new List<string>();

		/// <summary>
		/// Initializes the <see cref="Inflector"/> class.
		/// </summary>
		static Inflector()
		{
			AddPluralRule("$", "s");
			AddPluralRule("s$", "s");
			AddPluralRule("(ax|test)is$", "$1es");
			AddPluralRule("(octop|vir)us$", "$1i");
			AddPluralRule("(alias|status)$", "$1es");
			AddPluralRule("(bu)s$", "$1ses");
			AddPluralRule("(buffal|tomat)o$", "$1oes");
			AddPluralRule("([ti])um$", "$1a");
			AddPluralRule("sis$", "ses");
			AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves");
			AddPluralRule("(hive)$", "$1s");
			AddPluralRule("([^aeiouy]|qu)y$", "$1ies");
			AddPluralRule("(x|ch|ss|sh)$", "$1es");
			AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices");
			AddPluralRule("([m|l])ouse$", "$1ice");
			AddPluralRule("^(ox)$", "$1en");
			AddPluralRule("(quiz)$", "$1zes");

			AddSingularRule("s$", String.Empty);
			AddSingularRule("ss$", "ss");
			AddSingularRule("(n)ews$", "$1ews");
			AddSingularRule("([ti])a$", "$1um");
			AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
			AddSingularRule("(^analy)ses$", "$1sis");
			AddSingularRule("([^f])ves$", "$1fe");
			AddSingularRule("(hive)s$", "$1");
			AddSingularRule("(tive)s$", "$1");
			AddSingularRule("([lr])ves$", "$1f");
			AddSingularRule("([^aeiouy]|qu)ies$", "$1y");
			AddSingularRule("(s)eries$", "$1eries");
			AddSingularRule("(m)ovies$", "$1ovie");
			AddSingularRule("(x|ch|ss|sh)es$", "$1");
			AddSingularRule("([m|l])ice$", "$1ouse");
			AddSingularRule("(bus)es$", "$1");
			AddSingularRule("(o)es$", "$1");
			AddSingularRule("(shoe)s$", "$1");
			AddSingularRule("(cris|ax|test)es$", "$1is");
			AddSingularRule("(octop|vir)i$", "$1us");
			AddSingularRule("(alias|status)$", "$1");
			AddSingularRule("(alias|status)es$", "$1");
			AddSingularRule("^(ox)en", "$1");
			AddSingularRule("(vert|ind)ices$", "$1ex");
			AddSingularRule("(matr)ices$", "$1ix");
			AddSingularRule("(quiz)zes$", "$1");

			AddIrregularRule("person", "people");
			AddIrregularRule("man", "men");
			AddIrregularRule("child", "children");
			AddIrregularRule("sex", "sexes");
			AddIrregularRule("tax", "taxes");
			AddIrregularRule("move", "moves");

			AddUnknownCountRule("equipment");
			AddUnknownCountRule("information");
			AddUnknownCountRule("rice");
			AddUnknownCountRule("money");
			AddUnknownCountRule("species");
			AddUnknownCountRule("series");
			AddUnknownCountRule("fish");
			AddUnknownCountRule("sheep");
		}

		/// <summary>
		/// Adds the irregular rule.
		/// </summary>
		/// <param name="singular">The singular.</param>
		/// <param name="plural">The plural.</param>
		private static void AddIrregularRule(string singular, string plural)
		{
			AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1)));
			AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1)));
		}

		/// <summary>
		/// Adds the unknown count rule.
		/// </summary>
		/// <param name="word">The word.</param>
		private static void AddUnknownCountRule(string word)
		{
			_uncountables.Add(word.ToLower());
		}

		/// <summary>
		/// Adds the plural rule.
		/// </summary>
		/// <param name="rule">The rule.</param>
		/// <param name="replacement">The replacement.</param>
		private static void AddPluralRule(string rule, string replacement)
		{
			_plurals.Add(new InflectorRule(rule, replacement));
		}

		/// <summary>
		/// Adds the singular rule.
		/// </summary>
		/// <param name="rule">The rule.</param>
		/// <param name="replacement">The replacement.</param>
		private static void AddSingularRule(string rule, string replacement)
		{
			_singulars.Add(new InflectorRule(rule, replacement));
		}

		/// <summary>
		/// Makes the plural.
		/// </summary>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		public static string MakePlural(string word)
		{
			return ApplyRules(_plurals, word);
		}

		/// <summary>
		/// Makes the singular.
		/// </summary>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		public static string MakeSingular(string word)
		{
			return ApplyRules(_singulars, word);
		}

		/// <summary>
		/// Applies the rules.
		/// </summary>
		/// <param name="rules">The rules.</param>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		private static string ApplyRules(IList<InflectorRule> rules, string word)
		{
			string result = word;
			if (!_uncountables.Contains(word.ToLower()))
			{
				for (int i = rules.Count - 1; i >= 0; i--)
				{
					string currentPass = rules[i].Apply(word);
					if (currentPass != null)
					{
						result = currentPass;
						break;
					}
				}
			}
			return result;
		}

		/// <summary>
		/// Converts the string to title case.
		/// </summary>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		public static string ToTitleCase(string word)
		{
			return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"\b([a-z])",
					delegate(Match match) { return match.Captures[0].Value.ToUpper(); });
		}

		/// <summary>
		/// Converts the string to human case.
		/// </summary>
		/// <param name="lowercaseAndUnderscoredWord">The lowercase and underscored word.</param>
		/// <returns></returns>
		public static string ToHumanCase(string lowercaseAndUnderscoredWord)
		{
			return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " "));
		}


		/// <summary>
		/// Adds the underscores.
		/// </summary>
		/// <param name="pascalCasedWord">The pascal cased word.</param>
		/// <returns></returns>
		public static string AddUnderscores(string pascalCasedWord)
		{
			return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z\d])([A-Z])", "$1_$2"), @"[-\s]", "_").ToLower();
		}

		/// <summary>
		/// Makes the initial caps.
		/// </summary>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		public static string MakeInitialCaps(string word)
		{
			return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower());
		}

		/// <summary>
		/// Makes the initial lower case.
		/// </summary>
		/// <param name="word">The word.</param>
		/// <returns></returns>
		public static string MakeInitialLowerCase(string word)
		{
			return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1));
		}


		/// <summary>
		/// Determine whether the passed string is numeric, by attempting to parse it to a double
		/// </summary>
		/// <param name="str">The string to evaluated for numeric conversion</param>
		/// <returns>
		/// 	<c>true</c> if the string can be converted to a number; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsStringNumeric(string str)
		{
			double result;
			return (double.TryParse(str, NumberStyles.Float, NumberFormatInfo.CurrentInfo, out result));
		}

		/// <summary>
		/// Adds the ordinal suffix.
		/// </summary>
		/// <param name="number">The number.</param>
		/// <returns></returns>
		public static string AddOrdinalSuffix(string number)
		{
			if (IsStringNumeric(number))
			{
				int n = int.Parse(number);
				int nMod100 = n % 100;

				if (nMod100 >= 11 && nMod100 <= 13)
					return String.Concat(number, "th");

				switch (n % 10)
				{
					case 1:
						return String.Concat(number, "st");
					case 2:
						return String.Concat(number, "nd");
					case 3:
						return String.Concat(number, "rd");
					default:
						return String.Concat(number, "th");
				}
			}
			return number;
		}

		/// <summary>
		/// Converts the underscores to dashes.
		/// </summary>
		/// <param name="underscoredWord">The underscored word.</param>
		/// <returns></returns>
		public static string ConvertUnderscoresToDashes(string underscoredWord)
		{
			return underscoredWord.Replace('_', '-');
		}


		#region Nested type: InflectorRule

		/// <summary>
		/// Summary for the InflectorRule class
		/// </summary>
		private class InflectorRule
		{
			/// <summary>
			/// 
			/// </summary>
			public readonly Regex regex;

			/// <summary>
			/// 
			/// </summary>
			public readonly string replacement;

			/// <summary>
			/// Initializes a new instance of the <see cref="InflectorRule"/> class.
			/// </summary>
			/// <param name="regexPattern">The regex pattern.</param>
			/// <param name="replacementText">The replacement text.</param>
			public InflectorRule(string regexPattern, string replacementText)
			{
				regex = new Regex(regexPattern, RegexOptions.IgnoreCase);
				replacement = replacementText;
			}

			/// <summary>
			/// Applies the specified word.
			/// </summary>
			/// <param name="word">The word.</param>
			/// <returns></returns>
			public string Apply(string word)
			{
				if (!regex.IsMatch(word))
					return null;

				string replace = regex.Replace(word, replacement);
				if (word == word.ToUpper())
					replace = replace.ToUpper();

				return replace;
			}
		}

		#endregion

		public static string RemovePrefix(string cleanName)
		{
			return cleanName.Replace("c_", "").Replace("b_", "").Replace("sap_", "").Replace("mpl_", "");
		}
	}

}
